import fun.grammar.Grammar;
import fun.parser.Tree;
import fun.parser.earley.EarleyParser;
import fun.parser.earley.EarleyState;
import ic.ast.Node;

import java.util.List;

public abstract class BaseParser {
    protected Grammar grammar;

    public BaseParser(String grammar) {
        this.grammar = new Grammar(grammar);
    }

    protected int getLine(Tree tree) {
        return ((Token)tree.root).getLine();
    }

    protected int getColumn(Tree tree) {
        return ((Token)tree.root).getColumn();
    }

    protected String getValue(Tree tree) {
        return ((Token)tree.root).getValue();
    }

    protected void throwError(EarleyParser.PostMortem autopsy) {
        StringBuilder sb = new StringBuilder();
        Token token = (Token)autopsy.token;
        sb.append("expected ");
        if (autopsy.expecting.size() > 0) {
            boolean isFirst = true;
            for (String expected : autopsy.expecting) {
                if (!isFirst)
                    sb.append(" or ");
                else
                    isFirst = false;

                sb.append('\'');
                sb.append(expected);
                sb.append('\'');
            }
        } else {
            sb.append("end of input");
        }
        if (token == null)
        {
            throw new ParserException(sb.toString(), 0, 0);
        } else {
            sb.append(", but found \'");
            sb.append(token.getTag());
            sb.append('\'');
            throw new ParserException(sb.toString(), token.getLine(), token.getColumn());
        }
    }

    public fun.parser.Tree parse(Iterable<Token> tokens)
    {
        EarleyParser parser = new EarleyParser(tokens, grammar);
        List<EarleyState> pts = parser.getCompletedParses();
        if (pts.size() == 0) {
            throwError(parser.diagnoseError());
        }

        if (pts.size() != 1)
            throw new Error("parse error - num of trees: " + pts.size());
        return pts.get(0).parseTree();
    }

    protected abstract Node constructAst(fun.parser.Tree parseTree);

    public Node process(Iterable<Token> tokens)
    {
        return constructAst(parse(tokens));
    }
}
